package codeGeneration;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

import util.Common;
import util.FolderPath;
import util.HeaderOfJavaFile;

public class ControllerLayerGenerator extends Generator {
	private String defaultClassName;
	private String srcPath;
	private String packageName;
	private String beanName;
	private StringBuffer sbuffWriter;
	private HeaderOfJavaFile headerOfJavaFile;
	private String actionFormName;
	private String validatorClass;

	public ControllerLayerGenerator(FolderPath folderPath,
			String actionFormName, String validatorClass, String beanName) {
		super();
		this.packageName = folderPath.getPackageName();
		this.beanName = beanName;
		this.validatorClass = validatorClass;

		this.srcPath = folderPath.getSrcPath() + '/'
				+ packageName.replace('.', '/');
		defaultClassName = actionFormName + "Controller";
		this.actionFormName = actionFormName;

		sbuffWriter = new StringBuffer();
		headerOfJavaFile = new HeaderOfJavaFile();
		indentOneTime(HeaderOfJavaFile.packageNamePlaceHolder, null, ";\n",
				sbuffWriter, true);
		indentOneTime(HeaderOfJavaFile.importsListPlaceHolder, null, "",
				sbuffWriter, true);
	}

	public String generate() throws IOException {
		String viewName = null;
		viewName = generateClass();
		replacePlaceHolder(sbuffWriter, headerOfJavaFile);

		FileWriter fileWriter = null;
		try {
			fileWriter = getFileWriter(srcPath + '/' + defaultClassName
					+ ".java");
			fileWriter.append(sbuffWriter);
		} catch (IOException e) {
			e.printStackTrace();
			throw new IOException("Generate controller failed. "
					+ "Can't interact with controller file");
		}

		closeFileWriter(fileWriter);
		return viewName;
	}

	private String generateClass() {
		String homeViewName = null;
		headerOfJavaFile.setPackageName(packageName);
		headerOfJavaFile.addImportsList(packageName + ".bean." + beanName);
		headerOfJavaFile.addImportsList(packageName + ".validator."
				+ validatorClass);

		appendToFile(sbuffWriter, "@Controller\n");
		indentOneTime("public class " + defaultClassName, null, " {",
				sbuffWriter, false);

		headerOfJavaFile
				.addImportsList("org.springframework.stereotype.Controller");

		List<String> argumentTypes = new ArrayList<String>();
		argumentTypes.add("Model");
		List<String> arguments = new ArrayList<String>();
		arguments.add("model");

		homeViewName = generateHomeMethod("/", argumentTypes, arguments,
				"String");

		arguments.clear();
		argumentTypes.clear();

		headerOfJavaFile.addImportsList("org.springframework.web.bind."
				+ "annotation.ModelAttribute");
		argumentTypes.add("@ModelAttribute(\""
				+ Common.convertFirstLetterToLowerCase(beanName) + "\") "
				+ beanName);

		headerOfJavaFile.addImportsList("org.springframework."
				+ "validation.BindingResult");
		argumentTypes.add("BindingResult");

		arguments.add(Common.convertFirstLetterToLowerCase(beanName));
		arguments.add("result");

		headerOfJavaFile.addImportsList("org.springframework.web.bind."
				+ "annotation.RequestMethod");
		Properties propOfRequest = new Properties();
		propOfRequest.setProperty("method", "RequestMethod.POST");

		generateAddMethodAndValidator("/add", argumentTypes, arguments,
				"String", "result", homeViewName, propOfRequest);
		decreaseIndentOneTime("}", sbuffWriter);
		return homeViewName;
	}

	private String generateHomeMethod(String url, List<String> argumentTypes,
			List<String> arguments, String returnType) {
		String viewName = null;

		Properties prop = new Properties();
		prop.setProperty("value", "\"" + url + "\"");
		generateAnnotation("RequestMapping", prop);
		headerOfJavaFile.addImportsList("org.springframework."
				+ "web.bind.annotation.RequestMapping");
		headerOfJavaFile.addImportsList("org.springframework.ui.Model");

		appendToFile(sbuffWriter, "public " + returnType + " home" + " (");
		for (int i = 0; i < argumentTypes.size(); ++i) {
			sbuffWriter.append(argumentTypes.get(i) + ' ' + arguments.get(i));
			if (i < argumentTypes.size() - 1)
				sbuffWriter.append(' ');
		}

		sbuffWriter.append(") {\n");
		++indentIndex;

		appendToFile(sbuffWriter,
				beanName + ' ' + Common.convertFirstLetterToLowerCase(beanName));
		sbuffWriter.append(" = new " + beanName + "();\n");
		appendToFile(
				sbuffWriter,
				"model.addAttribute(\""
						+ Common.convertFirstLetterToLowerCase(beanName)
						+ "\", "
						+ Common.convertFirstLetterToLowerCase(beanName)
						+ ");\n");

		viewName = actionFormName + "View";
		appendToFile(sbuffWriter, "return \"" + viewName + "\";\n");

		--indentIndex;
		appendToFile(sbuffWriter, "}\n");
		return viewName;
	}

	private void generateAnnotation(String annotation, Properties prop) {
		StringBuffer sbuff = new StringBuffer();
		if (prop != null) {
			Enumeration<Object> keys = prop.keys();

			while (keys.hasMoreElements()) {
				String obj = (String) keys.nextElement();
				String value = prop.getProperty(obj);

				sbuff.append(obj + " = " + value + ", ");
			}
			sbuff.delete(sbuff.length() - 2, sbuff.length());
		}

		appendToFile(sbuffWriter, "@" + annotation + "(" + sbuff.toString()
				+ ")\n");
	}

	private void generateAddMethodAndValidator(String url,
			List<String> argumentTypes, List<String> arguments,
			String returnType, String resultOfValidator, String homeViewName,
			Properties propOfRequest) {
		sbuffWriter.append('\n');
		propOfRequest.setProperty("value", "\"" + url + "\"");
		generateAnnotation("RequestMapping", propOfRequest);

		appendToFile(sbuffWriter, "public " + returnType + " add" + beanName
				+ " (");

		for (int i = 0; i < argumentTypes.size(); ++i) {
			sbuffWriter.append(argumentTypes.get(i) + ' ' + arguments.get(i));
			if (i < argumentTypes.size() - 1)
				sbuffWriter.append(", ");
		}

		sbuffWriter.append(") {\n");
		++indentIndex;

		String beanValidatorObject = Common
				.convertFirstLetterToLowerCase(validatorClass);

		appendToFile(sbuffWriter, validatorClass + ' ' + beanValidatorObject);
		sbuffWriter.append(" = new " + validatorClass + "();\n");
		appendToFile(sbuffWriter, "if (" + beanValidatorObject + ".supports("
				+ validatorClass + ".class))\n");
		++indentIndex;

		appendToFile(
				sbuffWriter,
				beanValidatorObject + ".validate("
						+ Common.convertFirstLetterToLowerCase(beanName) + ", "
						+ resultOfValidator + ");\n");
		--indentIndex;

		appendToFile(sbuffWriter, "if(" + resultOfValidator + ".hasErrors())\n");
		++indentIndex;
		appendToFile(sbuffWriter, "return \"" + homeViewName + "\";\n\n");
		--indentIndex;

		appendToFile(sbuffWriter, "// TODO: Write code here\n");
		appendToFile(sbuffWriter, "// Need to redirect to other URL, "
				+ "there is POST request\n\n");

		appendToFile(sbuffWriter, "return " + "\"redirect:\"" + ";\n\n");
		--indentIndex;
		appendToFile(sbuffWriter, "}\n");

	}
}
